package bootstrap

import (
	"fmt"
	"gitee.com/guolianyu/kit/skit/third_party/config"
	"github.com/go-kratos/kratos/contrib/registry/consul/v2"
	"github.com/go-kratos/kratos/contrib/registry/discovery/v2"
	"github.com/go-kratos/kratos/contrib/registry/etcd/v2"
	"github.com/go-kratos/kratos/contrib/registry/nacos/v2"
	"github.com/go-kratos/kratos/contrib/registry/zookeeper/v2"
	"github.com/go-kratos/kratos/v2/registry"
	"github.com/go-zookeeper/zk"
	"github.com/hashicorp/consul/api"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/vo"
	etcdV3 "go.etcd.io/etcd/client/v3"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

/*
	服务注册
	etcd、nacos、consul、Zookeeper、Polaris
*/

const DefaultMicroServiceNameSpace = "/microservices"

func NewRegistrarProvider(cfg *config.Registry) registry.Registrar {
	/*
		筛选出使用的是哪一个服务注册中间件
	*/
	if cfg.GetEtcd() != nil {
		etcdClient := NewEtcdClient(cfg.GetEtcd())
		ns := GetMicroservicesNameSpace(cfg.GetEtcd().GetNamespace())
		return etcd.New(etcdClient, etcd.Namespace(ns))
	} else if cfg.GetConsul() != nil {
		consulClient := NewConsulClient(cfg.GetConsul())
		return consul.New(consulClient)
	} else if cfg.GetNacos() != nil {
		nacosClient := NewNacosClient(cfg.GetNacos())
		return nacos.New(nacosClient)
	} else if cfg.GetZookeeper() != nil {
		zookeeperRegistrar := NewZookeeperRegistrar(cfg.GetZookeeper())
		return zookeeperRegistrar
	} else if cfg.GetDiscovery() != nil {
		discoveryRegistrar := NewDiscoveryRegistrar(cfg.GetDiscovery())
		return discoveryRegistrar
	}
	return nil
}

func GetMicroservicesNameSpace(ns string) string {
	if ns == "" || ns == "/" {
		return DefaultMicroServiceNameSpace
	}
	return fmt.Sprintf("%s/microservices", ns)
}

// NewEtcdClient etcd
func NewEtcdClient(cfg *config.EtcdConfig) *etcdV3.Client {
	if cfg == nil {
		log.Panic("etcd registrar c must be set.")
	}
	c := etcdV3.Config{
		Endpoints:   cfg.Endpoints,
		DialTimeout: time.Second * 20,
	}
	if cfg.Username != nil {
		c.Username = cfg.GetUsername()
	}
	if cfg.Password != nil {
		c.Password = cfg.GetPassword()
	}
	if cfg.GetTimeout() != nil {
		c.DialTimeout = cfg.GetTimeout().AsDuration()
	}
	client, err := etcdV3.New(c)
	if err != nil {
		panic(err)
	}
	return client
}

// NewConsulClient Consul
func NewConsulClient(cfg *config.ConsulConfig) *api.Client {
	if cfg == nil {
		log.Panic("consul registrar config must be set.")
	}
	if len(cfg.GetEndpoints()) == 0 {
		log.Panic("consul endpoints config must be set more than one")
	}
	clientConfig := &api.Config{
		Address: cfg.GetEndpoints()[0],
		HttpClient: &http.Client{
			Timeout: time.Second * 20,
		},
		Token: cfg.Token,
	}
	if cfg.Password != nil && cfg.Username != nil {
		clientConfig.HttpAuth = &api.HttpBasicAuth{
			Username: cfg.GetUsername(),
			Password: cfg.GetPassword(),
		}
	}

	if cfg.GetTimeout() != nil {
		clientConfig.HttpClient = &http.Client{
			Timeout: cfg.GetTimeout().AsDuration(),
		}
	}

	client, err := api.NewClient(clientConfig)
	if err != nil {
		log.Panic("consul new client fail,er:", err.Error())
	}

	return client
}

// NewNacosClient Nacos
func NewNacosClient(cfg *config.NacosConfig) naming_client.INamingClient {
	if cfg == nil {
		log.Panic("nacos registrar config must be set.")
	}
	var sc []nacosConstant.ServerConfig
	for _, configHost := range cfg.GetEndpoints() {
		uri, _ := url.Parse(configHost)
		h := strings.Split(uri.Host, ":")
		addr := h[0]
		port, _ := strconv.Atoi(h[1])

		sc = append(sc, *nacosConstant.NewServerConfig(addr, uint64(port)))
	}
	client, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ServerConfigs: sc,
		},
	)

	if err != nil {
		log.Panic(err)
	}

	return client
}

// NewZookeeperRegistrar zookeeper
func NewZookeeperRegistrar(cfg *config.ZookeeperConfig) registry.Registrar {
	if cfg == nil {
		log.Panic("zookeeper registrar config must be set.")
	}
	timeout := time.Second * 15
	if cfg.Timeout != nil {
		timeout = cfg.GetTimeout().AsDuration()
	}
	conn, _, err := zk.Connect(cfg.Endpoints, timeout)
	if err != nil {
		log.Panic(err)
	}
	var opts []zookeeper.Option
	if cfg.Password != nil && cfg.Username != nil {
		opts = append(opts, zookeeper.WithDigestACL(cfg.GetUsername(), cfg.GetPassword()))
	}
	if cfg.Namespace != nil {
		opts = append(opts, zookeeper.WithRootPath(cfg.GetNamespace()))
	}

	return zookeeper.New(conn, opts...)
}

func NewDiscoveryRegistrar(cfg *config.DiscoveryConfig) registry.Registrar {
	if cfg == nil {
		log.Panic("discovery registrar config must be set.")
	}
	return discovery.New(&discovery.Config{
		Nodes:  cfg.GetEndpoints(),
		Region: cfg.GetEnv(),
		Zone:   cfg.GetZone(),
		Env:    cfg.GetEnv(),
		Host:   cfg.GetHost(),
	})
}
